package com.caochenlei.db.meta.core;

import com.caochenlei.db.exception.DataAccessException;
import com.caochenlei.db.exception.DatabaseMetaGetMetaException;
import com.caochenlei.db.meta.retriever.MetaCrawler;
import com.caochenlei.db.meta.schema.*;
import com.caochenlei.db.util.JDBCUtils;
import org.apache.commons.dbcp.BasicDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.Map;
import java.util.Set;

public class MetaLoaderImpl implements MetaLoader {
    private static Logger logger = LoggerFactory.getLogger(MetaLoaderImpl.class);
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        // C3P0
//		if (dataSource instanceof ComboPooledDataSource) {
//			ComboPooledDataSource cpd = (ComboPooledDataSource) dataSource;
//			cpd.getProperties().setProperty("remarks", "true");
//			cpd.getProperties().setProperty("useInformationSchema", "true");
//		}
        // DBCP
        if (dataSource instanceof BasicDataSource) {
            BasicDataSource bds = (BasicDataSource) dataSource;
            bds.addConnectionProperty("remarks", "true");
            bds.addConnectionProperty("useInformationSchema", "true");
        }
        // Druid
//		if (dataSource instanceof DruidDataSource) {
//			DruidDataSource dd = (DruidDataSource) dataSource;
//			dd.addConnectionProperty("remarks", "true");
//			dd.addConnectionProperty("useInformationSchema", "true");
//		}
        this.dataSource = dataSource;
    }

    private MetaCrawlerFactory factory = new DefaultMetaCrawlerFactory();

    public void setFactory(MetaCrawlerFactory factory) {
        this.factory = factory;
    }

    public MetaLoaderImpl() {
    }

    public MetaLoaderImpl(DataSource dataSource) {
        setDataSource(dataSource);
    }

    public Set<String> getTableNames() {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getTableNames();
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get tableNames error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Table getTable(String tableName) {
        return getTable(tableName, SchemaInfoLevel.standard());
    }

    public Table getTable(String tableName, SchemaInfoLevel level) {
        return getTable(tableName, level, null);
    }

    public Table getTable(String tableName, SchemaInfo schemaInfo) {
        return getTable(tableName, SchemaInfoLevel.standard(), schemaInfo);
    }

    public Table getTable(String tableName, SchemaInfoLevel schemaLevel, SchemaInfo schemaInfo) {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getTable(tableName, schemaLevel, schemaInfo);
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get table error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Set<SchemaInfo> getSchemaInfos() {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getSchemaInfos();
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get schemaInfos error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Schema getSchema() {
        return getSchema(SchemaInfoLevel.standard());
    }

    public Schema getSchema(SchemaInfoLevel level) {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getSchema(level);
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get schema error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Schema getSchema(SchemaInfo schemaInfo, SchemaInfoLevel level) throws DataAccessException {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getSchema(schemaInfo, level);
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get schema error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Schema getSchema(SchemaInfo schemaInfo) {
        return getSchema(schemaInfo, SchemaInfoLevel.standard());
    }

    public Database getDatabase(SchemaInfoLevel level) {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getDatabase(level);
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get database error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Database getDatabase() {
        return getDatabase(SchemaInfoLevel.standard());
    }

    private Set<String> getProcedureNames(SchemaInfo schemaInfo) {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getProcedureNames(schemaInfo);
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get procedureNames error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Set<String> getProcedureNames() {
        return getProcedureNames(null);
    }

    public Procedure getProcedure(String procedureName) {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getProcedure(procedureName);
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get procedure error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Map<String, Procedure> getProcedures() {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getProcedures();
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get procedures error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Set<String> getTriggerNames() throws DataAccessException {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getTriggerNames();
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get triggerNames error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Trigger getTrigger(String triggerName) throws DataAccessException {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getTrigger(triggerName);
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get trigger error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Map<String, Trigger> getTriggers() throws DataAccessException {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getTriggers();
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get triggers error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Set<String> getFunctionNames() throws DataAccessException {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getFunctionNames();
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get functionNames error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Function getFunction(String name) throws DataAccessException {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getFunction(name);
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get function error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }

    public Map<String, Function> getFunctions() throws DataAccessException {
        Connection con = JDBCUtils.getConnection(dataSource);
        MetaCrawler metaCrawler = null;
        try {
            metaCrawler = factory.newInstance(con);
            return metaCrawler.getFunctions();
        } catch (DataAccessException e) {
            logger.debug(e.getMessage(), e);
            throw new DatabaseMetaGetMetaException("Get functions error!", e);
        } finally {
            JDBCUtils.closeConnection(con);
        }
    }
}
